Imported Symbols Trouble

Shlomi Fish on 2008-01-18T16:53:00

After I udpated my Mandriva Cooker system a few days ago (after the perl package there had been upgraded to 5.10.0), I noticed that SpamAssassin's "spamd" and "spamassassin" started generating the following warnings:

Constant subroutine IO::Socket::INET6::AF_INET6 redefined at
/usr/lib/perl5/5.10.0/Exporter.pm line 66.
 at /usr/lib/perl5/vendor_perl/5.8.8/IO/Socket/INET6.pm line 16
Prototype mismatch: sub IO::Socket::INET6::AF_INET6 () vs none at
/usr/lib/perl5/5.10.0/Exporter.pm line 66.
 at /usr/lib/perl5/vendor_perl/5.8.8/IO/Socket/INET6.pm line 16
Constant subroutine IO::Socket::INET6::PF_INET6 redefined at
/usr/lib/perl5/5.10.0/Exporter.pm line 66.
 at /usr/lib/perl5/vendor_perl/5.8.8/IO/Socket/INET6.pm line 16
Prototype mismatch: sub IO::Socket::INET6::PF_INET6 () vs none at
/usr/lib/perl5/5.10.0/Exporter.pm line 66.
 at /usr/lib/perl5/vendor_perl/5.8.8/IO/Socket/INET6.pm line 16

After investigating, I found out that there was a "use Socket6;" call there, which imported the AF_INET6 and PF_INET6 symbols, which were already imported into the IO::Socket::INET6 package by previous Socket and IO::Socket calls.

In order to get rid of the warnings, I eventually decided to only selectively import symbols from Socket6, and imported the necessary symbols minus AF_INET6 and PF_INET6. In order to get IO::Socket::INET6 to pass its tests, I had to fix its tests, which had been broken before any modifications to it started. LeoNerd helped me with that, so thanks! Then I submitted a modified SRPM to Mandriva with a patch and went to sleep happy.

Today, when I woke up and checked my email, I was surprised to discover that about 20 spam mails landed at my inbox. As it turned out, they didn't have the SpamAssassin headers. Running spamc from the command line, yielded an unmodified message, while the standalone spamassassin program worked perfectly. So I started to investigate why spamc and spamd failed.

I ran into some trouble trying to point spamd to a modified IO::Socket::INET6 module, because it was tainted, and won't read the PERL5LIB environment variable (it's documented in perlrun. I had to modify the sources and use "use lib".

I also had a lot of trouble trying to see why IO::Socket::INET6 fails and where. This eventually was resolved by running spamd like this:

perl  -T blib/script/spamd -c -m5 -H --syslog="stderr info"

Then I could see the error clearly. Apparently I missed importing inet_ntop() from the Socket6 module, which was called by some functions that were not ran in the unit tests, and so it was only detected when spamd ran. This is one disadvantage of dynamic, symbolic languages such as Perl, and can be prevented in statically compiled languages such as C and as far as I know, Java as well.

Well, but since the code in question was Perl, I just added tests for the functions calling inet_ntop() and added it to the imports. After installing the modified module, spamd worked again, and I submitted a new patch for inclusion into Mandriva. So the enhancements now are that the warnings have been removed, the tests passing and the test suite made more robust.

As these modifications, may be useful for other platforms besides Mandriva, I sent a message to the maintainer and other designated parties regarding resuming the maintenance of IO::Socket::INET6.

Some other lessons from this:

  1. use MyModule () is your friend. use MyModule; and then using the symbols that are exported by default is more evil.
  2. If you can use the symbols from the original package. If you can't, import them selectively.
  3. Make sure you have a comprehensive test suite with good (preferabaly 100%) test coverage. Test-Driven Development is your friend.

Right now I'm cranky and happy at the same time, because I've spent half-a-day on this bug.